import request from './request';
import {
    assert,
    mergeOptions,
    clone
} from './common';
import defaultOptions from './options';
import createResponse from './response';
import createError from './error';
import Interceptor from './Interceptor';

const urlLib = require('url');
assert(false, 'sdfsdf');


class Axios {
    constructor() {
        const _this = this;

        this.interceptors = {
            request:new Interceptor(),
            response:new Interceptor()
        };
        return new Proxy(request, {
            get(data, name) {
                return _this[name];
            },
            set(data, name, val) {
                _this[name] = val;
                return true;
            },
            apply(fn, thisArg, args) {

                let options = _this._preProccess(undefined, args);
                if (!options) {
                    //Axios(url,{params:{},headers:{}})
                    if (args.length == 2) {

                        assert(typeof args[0] == 'string', 'args[0] must be a string');
                        assert(typeof args[1] == 'object' && args[1].constructor == Object, 'args[1] must be a object');
                        options = {
                            ...args[1],
                            url: args[0],
                            method: undefined
                        };


                    } else {
                        assert(false, 'invalid arguments');
                    }
                }

                return _this.request(options);
            },

        })
    }

    request(options) {

        //合并参数
        let _headers = this.default.headers;
        delete this.default.headers;
        let result = clone(this.default);



        mergeOptions(result, this.default);
        mergeOptions(result, options);
        this.default.headers = _headers;

        options = result;



        let headers = {};

        mergeOptions(headers, this.default.headers.common);
        mergeOptions(headers, this.default.headers[options.method.toLowerCase()]);
        mergeOptions(headers, options.headers);

        options.headers = headers;

        checkOptions(options);

        options.url = urlLib.resolve(options.baseUrl, options.url);

        const {
            transformRequest,
            transformResponse
        } = options;
        delete options.transformRequest;
        delete options.transformResponse;
        delete options.baseUrl;

        options = transformRequest(options);
        checkOptions(options);

        let list = this.interceptors.request.getList();

        list.forEach(fn=>{
            options = fn(options);
            checkOptions(options);
        })



        return new Promise((resolve, reject) => {
            request(options).then(xhr => {
                let res = createResponse(xhr);
                res.data = transformResponse(res.data);
                let list = this.interceptors.response.getList();
                list.forEach(fn=>{
                    res = fn(res);
                })
                resolve(res);
            }, xhr => {
                let error = createError(xhr);
                reject(error);
            })
        })



    }

    //处理公共参数
    _preProccess(method, args) {
        let options;
        if (args.length == 1 && typeof args[0] == 'string') {

            options = {
                method,
                url: args[0]
            };

        } else if (args.length == 1 && typeof args[0] == 'object' && args[0].constructor == Object) {
            options = {
                ...args[0],
                method
            };
        }

        return options;
    }

    get(...args) {
        let options = this._preProccess('get', args);
        if (!options) {
            //get(url,{params:{},headers:{}})
            if (args.length == 2) {

                assert(typeof args[0] == 'string', 'args[0] must be a string');
                assert(typeof args[1] == 'object' && args[1].constructor == Object, 'args[1] must be a object');
                options = {
                    ...args[1],
                    url: args[0],
                    method: 'get'
                };


            } else {
                assert(false, 'arguments length must be 2');
            }
        }
        return this.request(options);
    }

    post(...args) {
        let options = this._preProccess('post', args);
        if (!options) {
            /*
                post(url,{a:1,b：2})
                post(url,{a:1,b:2},{params:{},headers:{}})
            */
            if (args.length == 2) {

                assert(typeof args[0] == 'string', 'args[0] must be a string');

                options = {
                    url: args[0],
                    data: args[1],
                    method: 'post'
                }

            } else if (args.length == 3) {
                assert(typeof args[0] == 'string', 'args[0] must be a string');
                assert(typeof args[2] == 'object' && args[2].constructor == Object, 'args[2] must be a object');
                options = {
                    ...args[2],
                    data: args[1],
                    url: args[0],
                    method: 'post'
                };
            } else {
                assert(false, 'invalid argumnets')
            }
        }

        return this.request(options);
    }

    delete(...args) {
        let options = this._preProccess('delete', args);
        if (!options) {
            //delete(url,{params:{},headers:{}})
            if (args.length == 2) {

                assert(typeof args[0] == 'string', 'args[0] must be a string');
                assert(typeof args[1] == 'object' && args[1].constructor == Object, 'args[1] must be a object');
                options = {
                    ...args[1],
                    url: args[0],
                    method: 'delete'
                };


            } else {
                assert(false, 'arguments length must be 2');
            }
        }
        return this.request(options);
    }
}


Axios.create = Axios.prototype.create = function (options) {

    let axios = new Axios();

    let _options = clone(defaultOptions);

    axios.default = mergeOptions(_options, options);

    return axios;
}

function checkOptions(options) {
    assert(options, 'options must be required');
    assert(options.method, 'no method');
    assert(typeof options.method, 'method must be a string');
    assert(options.url, 'no url');
    assert(typeof options.url == 'string', 'url must be a string');
};


export default Axios.create();